package com.xyy.saas.payment.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.xyy.saas.payment.adapter.channel.fm.config.BankConfigManager;
import com.xyy.saas.payment.constants.Constants;
import com.xyy.saas.payment.enums.BusinessType;
import com.xyy.saas.payment.enums.CostType;
import com.xyy.saas.payment.constants.HttpStatus;
import com.xyy.saas.payment.manage.cores.bo.SyncFMPayReportConfigBO;
import com.xyy.saas.payment.pojo.vo.PayCheckQueryParamVo;
import com.xyy.saas.payment.pojo.vo.PayCheckVo;
import com.xyy.saas.payment.manage.cores.vo.ResultPageVo;
import com.xyy.saas.payment.dao.model.PayCheckQueryParam;
import com.xyy.saas.payment.dao.model.PayReport;
import com.xyy.saas.payment.service.report.PayCheckQueryService;
import com.xyy.saas.payment.service.PayCheckService;
import com.xyy.saas.payment.util.ExportExcelUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author zhangwenxiong
 * @Date 2020/10/22 16:16
 */
@Slf4j
@Service
public class PayCheckServiceImpl implements PayCheckService {

    @Autowired
    private PayCheckQueryService payCheckQueryService;
    @Autowired
    private BankConfigManager bankConfigManager;

    /**
     * 结果使用
     */
    private SimpleDateFormat sdfResult = new SimpleDateFormat("yyyyMMdd HH:mm:ss");

    // 账号类型和描述信息的映射关系
    private Map<String, String> accountTypeMap = new HashMap<>();

    @Override
    public ResultPageVo<List<PayCheckVo>> queryByPage(String jsonStr) {
        // 拉去 accountTypeMap 映射关系
        accountTypeMap = bankConfigManager.getFuminAccountConfig().stream().collect(Collectors.toMap(SyncFMPayReportConfigBO::getAccountType,SyncFMPayReportConfigBO::getDesc));

        ResultPageVo result = new ResultPageVo();
        try {
            if (StringUtils.isNotEmpty(jsonStr)) {
                PayCheckQueryParamVo paramVo = JSONObject.parseObject(jsonStr, PayCheckQueryParamVo.class);
                //将前台参数转换成规范格式,并对参数做校验
                PayCheckQueryParam param = checkParam(paramVo,result,"query");
                if (0 == result.getCode()){
                    //查询出总数
                    Long total = payCheckQueryService.getTotal(param);
                    if ((null == total || 0 == total) && null == result){
                        result = ResultPageVo.createSuccess();
                    }else if (0 < total){
                        List<PayReport> reports = payCheckQueryService.queryAll(param);
                        List<PayCheckVo> vos = poToVo(reports);
                        result = ResultPageVo.createSuccess(param.getPage(),param.getPageSize(),total,vos);
                    }
                }
            } else {
                result = ResultPageVo.createFail("未检测到入参,请检查");
            }

        }catch (Exception e){
            log.error("查询支付对账信息异常:{}",e);
            result = ResultPageVo.createFail("查询异常,请联系管理员");
        }
        return result;
    }

    @Override
    public void export(String jsonStr, HttpServletRequest request,
                                   HttpServletResponse response) {
        ResultPageVo pageVo = null;
        try {
            if (StringUtils.isNotEmpty(jsonStr)) {
                PayCheckQueryParamVo paramVo = JSONObject.parseObject(jsonStr, PayCheckQueryParamVo.class);
                //将前台参数转换成规范格式,并对参数做校验
                PayCheckQueryParam param = checkParam(paramVo,pageVo,"export");
                if (null == pageVo){
                    //查询出总数
                    Long total = payCheckQueryService.getTotal(param);
                    if (300000 < total){
                        log.error("一次导出最多不能超过30W条数据");
                        response.setStatus(HttpStatus.DATA_MAX);
                        return;
                    }else if (0 <= total){
                        //查询数据
                        List<PayReport> reports = payCheckQueryService.queryAll(param);
                        List<PayCheckVo> vos = poToVo(reports);
                        //拼装文件名
                        String filname = buildFileName(param);

                        //导出数据
                        ExportExcelUtil.createExcelDateFormat(response, request, filname, "支付对账表", Constants.PAY_CHECK_HEADER, Constants.PAY_CHECK_FILED, vos, true, null);
                    }
                }else {
                    response.setStatus(HttpStatus.PARAM_ERROR);
                    return;
                }
            }

        }catch (Exception e){
            log.error("查询支付对账信息异常:{}",e);
            response.setStatus(HttpStatus.SYSTEM_BUSY);
            return;
        }
    }

    //导出不需要分页,但是单次最多导出10W条数据
    private PayCheckQueryParam checkParam(PayCheckQueryParamVo paramVo,ResultPageVo result,String type){
        PayCheckQueryParam param = new PayCheckQueryParam();
        if (StringUtils.isEmpty(paramVo.getDealEndTime()) ||
            StringUtils.isEmpty(paramVo.getDealStartTime()) ||
            StringUtils.isEmpty(paramVo.getBusinessType()) ||
            StringUtils.isEmpty(paramVo.getAccountType())){
            result.setFail("交易开始时间,结束时间,业务场景,账户类型必填");
            return null;
        }

        if ((0 == paramVo.getPage() || 0 == paramVo.getPageSize()) && !"export".equals(type)){
            result.setFail("分页参数错误,请检查");
            return null;
        }

        try {
            //一天的毫秒数
            int dayMis=1000*60*60*24;
            Date startDate = new Date(Long.valueOf(paramVo.getDealStartTime()));
            Date endDate = new Date(Long.valueOf(paramVo.getDealEndTime()));

            //查询条件结束时间变为最后结束时间当天的最后一秒
            Long endMill = endDate.getTime();
            Long lastMill = endMill + (dayMis - 1);
            endDate = new Date(lastMill);

            //查询的时间范围不能超过31天
            Calendar start = Calendar.getInstance();
            Calendar end = Calendar.getInstance();
            start.setTime(startDate);
            start.add(Calendar.DATE,31);
            end.setTime(endDate);
            if (start.before(end)){
                result.setFail("查询时间超过31天");
                return null;
            }

            param.setAccountName(paramVo.getAccountName());
            param.setAccountType(paramVo.getAccountType());
            param.setBusinessNo(paramVo.getBusinessNo());
            param.setCostType(paramVo.getCostType());
            param.setBusinessType(paramVo.getBusinessType());
            param.setDealEndTime(endDate);
            param.setDealStartTime(startDate);
            param.setMerchantName(paramVo.getMerchantName());
            param.setOppositeAccount(paramVo.getOppositeAccount());
            param.setOppositeName(paramVo.getOppositeName());
            param.setAccountIn(paramVo.getAccountIn());
            param.setAccountInName(paramVo.getAccountInName());
            param.setPayNo(paramVo.getPayNo());
            if (!"export".equals(type)) {
                param.setPage(paramVo.getPage());
                param.setPageSize(paramVo.getPageSize());
                param.setOffset((paramVo.getPage() - 1) * paramVo.getPageSize());
            }
        }catch (Exception e){
            throw new RuntimeException(e);
        }
        return param;
    }

    private List<PayCheckVo>  poToVo(List<PayReport> pos){
        List<PayCheckVo> vos = new ArrayList<>();
        if (!CollectionUtils.isEmpty(pos)){
            pos.forEach(po->{
                PayCheckVo vo = new PayCheckVo();
                vo.setAccountName(null == po.getAccountName()?"":po.getAccountName());
                vo.setAccountType(accountTypeMap.getOrDefault(po.getAccountType(), "未知账户"));
                vo.setAmount(null == po.getAmount()?"0.00":po.getAmount().toPlainString());
                vo.setBusinessNo(null == po.getBusinessNo()?"--":po.getBusinessNo());
                vo.setBusinessType(BusinessType.valueOf(po.getBusinessType()).getDesc());
                vo.setCostType(accountTypeMap.getOrDefault(po.getAccountType(), "未知账户")+ CostType.valueOf(po.getCostType()).getDesc());
                vo.setDealTime(sdfResult.format(po.getDealTime()));
                vo.setMerchantName(null == po.getMerchantName()?"--":po.getMerchantName());
                if (StringUtils.isEmpty(po.getOppositeAccount()) || po.getOppositeAccount().length()<8){
                    vo.setOppositeAccount("--");
                }else{
                    if (po.getAccountType() != null && po.getAccountType().contains("平安")) {
                        vo.setOppositeAccount(po.getOppositeAccount());
                    } else {
                        vo.setOppositeAccount(hideStr(po.getOppositeAccount()));
                    }
                }
                vo.setOppositeName(null == po.getOppositeName()?"":po.getOppositeName());

                if (StringUtils.isEmpty(po.getAccountIn()) || po.getAccountIn().length()<8){
                    vo.setAccountIn("--");
                }else{
                    if (po.getAccountType() != null && po.getAccountType().contains("平安")) {
                        vo.setAccountIn(po.getAccountIn());
                    } else {
                        vo.setAccountIn(hideStr(po.getAccountIn()));
                    }
                }
                vo.setAccountInName(null == po.getAccountInName()?"":po.getAccountInName());
                vo.setPayNo(null == po.getPayNo()?"--":po.getPayNo());
                vo.setRemark(null == po.getRemark()?"":po.getRemark());
                vo.setResidual(null == po.getResidual()?"0.00":po.getResidual().toEngineeringString());
                vos.add(vo);
            });
        }
        return vos;
    }

    private String buildFileName(PayCheckQueryParam param){
        StringBuffer sb = new StringBuffer();
        SimpleDateFormat sdfExport = new SimpleDateFormat("yyyyMMdd");
        sb.append(sdfExport.format(param.getDealStartTime()));
        sb.append("-");
        sb.append(sdfExport.format(param.getDealEndTime()));
        sb.append("-");
        sb.append(BusinessType.valueOf(param.getBusinessType()).getDesc());
        sb.append("-");
        sb.append(accountTypeMap.getOrDefault(param.getAccountType(), "未知账户"));
        sb.append("对账.xlsx");
        return sb.toString();
    }

    private String hideStr(String str){
        char[] chars = str.toCharArray();
        StringBuffer sb = new StringBuffer();
        for (int i=0;i<str.length();i++){
            if (i>3 && i<str.length()-4){
                sb.append("*");
            }else {
                sb.append(chars[i]);
            }
        }
        return sb.toString();
    }
}
